package com.example.demo.Utils;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.util.StringUtils;
import com.example.demo.config.MinioConfig;
import com.example.demo.handler.WordHandler;
import com.sun.scenario.effect.ImageData;
import io.minio.*;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.InetAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.net.UnknownHostException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @Author: lu
 * @Date: 2024-03-25-22:58
 * @Description:  创建Bucket、上传、下载数据到MinIO服务
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class MinioUtils {

    @Resource
    private MinioClient minioClient;


    @Resource
    MinioConfig minioConfig;

    @Value("${conch.minio.endpoint}")
    private String endpoint;

    /**
     * 生成唯一ID
     *
     * @param objectName 文件名
     * @return 唯一ID
     */
    private static String getFileName(String objectName) {
        //判断文件最后一个点所在的位置
        int lastIndexOf = objectName.lastIndexOf(".");
        if (lastIndexOf == -1) {
            return StrUtil.format("{}_{}", objectName, System.currentTimeMillis());
        } else {
            // 获取文件前缀,已最后一个点进行分割
            String filePrefix = objectName.substring(0, objectName.lastIndexOf("."));
            // 获取文件后缀,已最后一个点进行分割
            String fileSuffix = objectName.substring(objectName.lastIndexOf(".") + 1);
            // 组成唯一文件名
            return StrUtil.format("{}_{}.{}", filePrefix, System.currentTimeMillis(), fileSuffix);
        }
    }

    /**
     * Excel所有图片设置
     *
     * @param fileList
     * @return
     * @throws IOException
     */
    //public static WriteCellData<Void> imageCells(List<MultipartFile> fileList) throws IOException {
    //    // 可以放入多个图片
    //    List<ImageData> imageDataList = new ArrayList<>();
    //    int i = 0;
    //    for (MultipartFile file : fileList) {
    //        byte[] bytes = file.getBytes();
    //        // 这里可以设置为 EMPTY 则代表不需要其他数据了
    //        //writeCellData.setType(CellDataTypeEnum.EMPTY);
    //        ImageData imageData = new ImageData();
    //        // 设置图片
    //        imageData.setImage(bytes);
    //        // 图片类型
    //        //imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
    //        // 上 右 下 左 需要留空，这个类似于 css 的 margin；这里实测 不能设置太大 超过单元格原始大小后 打开会提示修复。暂时未找到很好的解法。
    //        imageData.setTop(1);
    //        imageData.setRight(1);
    //        imageData.setBottom(1);
    //        imageData.setLeft(1);
    //        // * 设置图片的位置。Relative表示相对于当前的单元格index。first是左上点，last是对角线的右下点，这样确定一个图片的位置和大小。
    //        // 目前填充模板的图片变量是images，index：row=7,column=0。所有图片都基于此位置来设置相对位置
    //        // 第1张图片相对位置
    //        imageData.setRelativeFirstRowIndex(i);
    //        imageData.setRelativeFirstColumnIndex(0);
    //        imageData.setRelativeLastRowIndex(i);
    //        imageData.setRelativeLastColumnIndex(0);
    //        imageDataList.add(imageData);
    //        i++;
    //    }
    //    WriteCellData<Void> writeCellData = new WriteCellData<>();
    //    writeCellData.setImageDataList(imageDataList);
    //    return writeCellData;
    //}

    /**
     * 判断桶是否存在
     *
     * @param bucketName bucket名称
     * @return true存在，false不存在
     */
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("检查桶是否存在失败!", e);
            throw new RuntimeException("检查桶是否存在失败！");
        }
    }

    /**
     * 创建bucket
     *
     * @param bucketName bucket名称
     */
    public void createBucket(String bucketName) {
        if (!this.bucketExists(bucketName)) {
            try {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            } catch (Exception e) {
                log.error("创建桶失败!", e);
                throw new RuntimeException("创建桶失败！");
            }
        }
    }

    /**
     * 上传MultipartFile文件到全局默认文件桶中
     *
     * @param bucketName 桶
     * @param catalog    文件夹
     * @param file       文件
     * @return 文件对应的URL
     */
    public String putObject(String bucketName, String catalog, MultipartFile file) throws UnknownHostException {
        // 给文件名添加时间戳防止重复
        String originalFilename = file.getOriginalFilename();
        String fileName = getFileName(Objects.requireNonNull(originalFilename));
        // 开始上传
        String objectName = this.buildObject(catalog, fileName);
        try (InputStream stream = file.getInputStream()) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            //.contentType(file.getContentType())
                            .contentType("image/png")
                            .stream(stream, stream.available(), -1)
                            .build()
            );
        } catch (Exception e) {
            log.error("文件流上传错误!", e);
            throw new RuntimeException("文件上传失败！");
        }
        String hostAddress = InetAddress.getLocalHost().getHostAddress();
        //return StrUtil.format("http://10.12.32.173:38098/{}{}",bucketName, objectName);
        return minioConfig.getEndpoint() + "/" + bucketName + objectName;
    }

    /**
     * 上传MultipartFile文件到全局默认文件桶中覆盖原有的文件
     *
     * @param bucketName 桶
     * @param catalog    文件夹
     * @param file       文件
     * @return 文件对应的URL
     */
    public String putObjectCover(String bucketName, String catalog, MultipartFile file) throws UnknownHostException {
        // 给文件名添加时间戳防止重复
        String fileName = file.getOriginalFilename();
        // 开始上传
        String objectName = this.buildObject(catalog, fileName);
        try (InputStream stream = file.getInputStream();) {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .contentType(file.getContentType())
                            .stream(stream, stream.available(), -1)
                            .build()
            );
        } catch (Exception e) {
            log.error("文件流上传错误!", e);
            throw new RuntimeException("文件上传失败！");
        }
        String hostAddress = InetAddress.getLocalHost().getHostAddress();
        //return StrUtil.format("http://10.12.32.173:38098/{}{}",bucketName, objectName);
        return minioConfig.getEndpoint() + "/" + bucketName + objectName;
    }

    /**
     * 判断文件是否存在
     *
     * @param bucketName 桶名称
     * @param catalog    文件夹
     * @param fileName   文件名称
     * @return true存在, 反之
     */
    public boolean checkFileIsExist(String bucketName, String catalog, String fileName) {
        try {
            String objectName = this.buildObject(catalog, fileName);
            minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(objectName).build()
            );
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 判断文件夹是否存在
     *
     * @param bucketName 桶名称
     * @param catalog    文件夹名称
     * @return true存在, 反之
     */
    public Boolean checkFolderIsExist(String bucketName, String catalog) {
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs
                            .builder()
                            .bucket(bucketName)
                            .prefix(catalog)
                            .recursive(false)
                            .build());
            for (Result<Item> result : results) {
                Item item = result.get();
                if (item.isDir() && Objects.equals(catalog, item.objectName())) {
                    return true;
                }
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 获取全部bucket
     *
     * @return 所有桶信息
     */
    public List<Bucket> getAllBuckets() {
        try {
            return minioClient.listBuckets();
        } catch (Exception e) {
            log.error("获取全部存储桶失败!", e);
            throw new RuntimeException("获取全部存储桶失败！");
        }
    }

    /**
     * 根据bucketName获取信息
     *
     * @param bucketName bucket名称
     * @return 单个桶信息
     */
    public Optional<Bucket> getBucket(String bucketName) {
        try {
            return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
        } catch (Exception e) {
            log.error("根据存储桶名称获取信息失败!", e);
            throw new RuntimeException("根据存储桶名称获取信息失败！");
        }
    }

    /**
     * 根据bucketName删除信息
     *
     * @param bucketName bucket名称
     */
    public void removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("根据存储桶名称删除桶失败!", e);
            throw new RuntimeException("根据存储桶名称删除桶失败！");
        }
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param catalog    文件夹
     * @param fileName   文件名称
     */
    public boolean removeObject(String bucketName, String catalog, String fileName) {
        try {
            String objectName = this.buildObject(catalog, fileName);
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * @param catalog  文件夹
     * @param fileName 文件名称 index.html
     * @return
     */
    private String buildObject(String catalog, String fileName) {
        return StrUtil.format("/{}/{}", catalog, fileName);
    }

    /**
     * 获取文件信息, 如果抛出异常则说明文件不存在
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
     */
    public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception {
        return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return url
     */
    @SneakyThrows
    public String getObjectUrl(String bucketName, String objectName) {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return
     */
    @SneakyThrows
    public List<Item> getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
        List<Item> list = new ArrayList<>();
        Iterable<Result<Item>> objectsIterator = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).prefix(prefix)
                        .recursive(recursive).build()
        );
        if (objectsIterator != null) {
            for (Result<Item> result : objectsIterator) {
                Item item = result.get();
                list.add(item);
            }
        }
        return list;
    }

    public void download(String bucketName, String objectName, HttpServletResponse response) {
        try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
             OutputStream outputStream = response.getOutputStream()) {
            response.reset();
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(objectName, "UTF-8"));
            response.addHeader("Access-Control-Allow-Origin", "*");
            response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
            response.addHeader("Access-Control-Allow-Headers", "Content-Type");
            if (objectName.endsWith(".svg")) {
                response.setContentType("image/svg+xml");
            } else if (objectName.endsWith(".gif")) {
                response.setContentType("image/gif");
            } else if (objectName.endsWith(".pdf")) {
                response.setContentType("application/pdf");
            } else {
                response.setContentType("application/octet-stream; charset=UTF-8");
            }
            IoUtil.copy(inputStream, outputStream);
        } catch (Exception e) {
            log.error("文件下载异常！", e);
            throw new RuntimeException("文件下载异常！");
        }
    }

    public File downloadForFileInputStream(String bucketName, String objectName) {
        try {
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
            /*这里实际上本地只能拿到/deviceems,linux远程也只能拿到/；不能多余写moudel层*/
            String property = System.getProperty("user.dir");
            String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
            File tempFile = new File(property + "/temporary/photos/" + fileName);
            tempFile.deleteOnExit();

            try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }

            return tempFile;
        } catch (Exception e) {
            log.error("图片下载异常！", e);
            throw new RuntimeException("图片下载异常！");
        }
    }


    /**
     * 下载临时图片到本地
     *
     * @param
     */
    public String downloadToFilePath(String bucketName, String objectName) {
        try {
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
            String property = System.getProperty("user.dir");
            String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
            File tempFile = new File(property + "/temporary/photos/" + fileName);
            tempFile.deleteOnExit();
            try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }
            return property + "/temporary/photos/" + fileName;
        } catch (Exception e) {
            throw new RuntimeException("图片下载异常！");
        }
    }

    /**
     * 下载临时图片到本地(返回WriteCellData用于嵌入在excel里）
     *
     * @param
     */
    //public WriteCellData<Void> downloadForMultipartFile(String bucketName, List<String> filesList) {
    //    try {
    //        List<MultipartFile> list = new ArrayList();
    //        for (String files : filesList) {
    //            String objectName = files.substring(18);
    //            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
    //            String property = System.getProperty("user.dir");
    //            String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
    //            File file = new File(property + "/temporary/files/" + fileName);
    //            MultipartFile multipartFile = new MockMultipartFile(file.getName(), inputStream);
    //            list.add(multipartFile);
    //        }
    //        WriteCellData<Void> writeCellData = imageCells(list);
    //        return writeCellData;
    //    } catch (Exception e) {
    //        log.error("图片下载异常！", e);
    //        throw new RuntimeException("图片下载异常！");
    //    }
    //}
    //
    ///*
    // * 个性化设置上传递路径
    // * */
    //public String uploadToMinioBySpecUrl(String url, MultipartFile file) {
    //    try {
    //        return putObject("datalake", url, file);
    //    } catch (Exception e) {
    //        throw new EmsException("上传失败");
    //    }
    //}


    /*
     * （单个）（缩短文件路径）：省略前置ip和端口
     * */
    @SuppressWarnings("all")
    public String subUrl(String originUrl) {
        /*缩短数据库保存容量http://10.12.32.173:38098*/
        /*（安全性）*/
        if (originUrl == null || originUrl.isEmpty()) {
            return null;
        }
        String finalUrl = originUrl.replaceAll(endpoint + "/", "");
        return finalUrl;
    }


    /*
     * （批量/单个）（缩短文件路径）：批量处理文件地址
     * */
    @SuppressWarnings("all")
    public String subMultUrls(String originTextUrls) {
        /*（安全性）直接调用此方法还需要处理空情况*/
        if (originTextUrls == null || originTextUrls.isEmpty()/*长度是否为0*/) {
            return null;
        }
        String[] originText = originTextUrls.split(","); /* 分割字符串*/
        StringBuilder finalText = new StringBuilder();
        for (String part : originText) {
            if (finalText.length() > 0) {
                finalText.append(","); /*添加逗号分隔*/
            }
            String newPart = part.replaceAll(endpoint + "/", "");
            finalText.append(newPart);
        }
        return String.valueOf(finalText);
    }

    /*
     * （批量/单个）在分页查询的时候将文件路径完整展示
     * */
    @SuppressWarnings("all")
    public String backTextUrls(String originTextUrls) {
        /*（安全性）可调用前过滤*/
        if (originTextUrls == null || originTextUrls.isEmpty()) {
            return null;
        }
        /*按照逗号,拼接字符*/
        String[] originText = originTextUrls.split(","); /* 分割字符串*/
        StringBuilder finalText = new StringBuilder();
        for (String part : originText) {
            if (finalText.length() > 0) {
                finalText.append(","); /*添加逗号分隔*/
            }
            finalText.append(endpoint).append("/").append(part); /*拼接 'a' 和数字*/
        }
        /*处理过的文件赋值 */
        return String.valueOf(finalText);
    }

    public File downloadFileInputStream(String bucketName, String objectName) {
        try {
            InputStream inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
            /*多层级文件处理*/
            String property = System.getProperty("user.dir");
            String fileName = objectName.substring(objectName.lastIndexOf("/") + 1);
            File tempFile = new File(property + "/temporary/photos/" + fileName);
            //tempFile.deleteOnExit();

            try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }

            return tempFile;
        } catch (Exception e) {
            log.error("图片下载异常！", e);
            throw new RuntimeException("图片下载异常！");
        }
    }


    /*
     * word转pdf
     * */
    public void downFileByPDF(HttpServletResponse response, String url) throws Exception {
        List<String> pathList = new ArrayList<>();
        String fileName = "";
        String str = "";
        try {
            // 提取 URL 的部分路径
            // 找到第一个 "datalake" 的位置
            int firstDatalakeIndex = url.indexOf("datalake");
            if (firstDatalakeIndex != -1) {
                // 从第一个 "datalake" 之后开始找第二个 "datalake"
                int secondDatalakeIndex = url.indexOf("datalake", firstDatalakeIndex + 1);
                if (secondDatalakeIndex != -1) {
                    // 从第二个 "datalake" 开始截取
                    str = url.substring(secondDatalakeIndex);
                }
            }
            // 使用 Minio 工具下载文件到本地路径
            String path = downloadToFilePath("datalake", str);
            File file = new File(path);
            pathList.add(path);
            // 提取文件名
            // 找到最后一个斜杠的位置
            int lastSlashIndex = path.lastIndexOf("/");
            // 找到最后一个点的位置
            int lastDotIndex = path.lastIndexOf(".");
            // 截取内容
            if (lastSlashIndex != -1 && lastDotIndex != -1 && lastDotIndex > lastSlashIndex) {
                fileName = path.substring(lastSlashIndex + 1, lastDotIndex);
            } else {
                throw new Exception("路径格式不正确");
            }
            WordHandler.wordToPdf1(response, path, fileName);
        } finally {
            //删除本地文件
            WordHandler.deleteDownloadedFile(pathList);
        }
    }




}
